package dynamic.wrapper.interfaces;

import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.yulichang.wrapper.resultmap.MFunc;
import dynamic.metadata.EntityClass;
import dynamic.metadata.EntityProperty;
import dynamic.toolkit.support.DColumnCache;
import dynamic.wrapper.resultmap.DLabel;
import dynamic.wrapper.resultmap.DMybatisLabel;
import dynamic.wrapper.resultmap.DMybatisLabelFree;

import java.util.Collection;
import java.util.Map;

/**
 * 对一或对多查询
 *
 * @author yulichang
 * @since 1.3.0
 */
@SuppressWarnings({"unchecked", "unused", "DuplicatedCode"})
public interface DQueryLabel<Children> {

    void addLabel(DLabel label);

    Children getChildren();

    /**
     * 一对多查询 调用此方法发必需要调用对应的 left join / right join ... 连表方法，否则会报错
     * <p>
     * 举例 UserDO AddressDO 为一对多关系  UserDTO 为结果类
     * <pre>
     *     MPJLambdaQueryWrapper<UserDO> wrapper = new MPJLambdaQueryWrapper<UserDO>();
     *     wrapper.selectAll(UserDO.class)
     *            .selectCollection(AddressDO.class, UserDTO::getAddressListDTO)
     *            .leftJoin(AddressDO.class, ...... )
     *            .eq(...)
     *            ...
     * <pre/>
     * 会自动将 AddressDO类中相同属性的字段 以mybatis<collection>的方式映射到UserDTO.addressListDTO属性中
     *
     * @since 1.3.0
     *
     * @param child    连表数据库实体类
     * @param dtoField 包装类对应的属性
     * @param <S>      包装类
     * @param <C>      对多数据库实体类
     * @param <Z>      包装类集合泛型
     * @param <F>      包装类集合字段泛型
     */
    default <S, C, Z, F extends java.util.Collection<?>> Children selectCollection(EntityClass child, EntityProperty dtoField) {
        return selectCollection(null, child, dtoField);
    }

    default <S, C, Z, F extends java.util.Collection<?>> Children selectCollection(String prefix, EntityClass child, EntityProperty dtoField) {
        String dtoFieldName = dtoField.getPropertyName();
        EntityClass dtoClass = dtoField.getEntityClass();
        Map<String, EntityProperty> fieldMap = dtoClass.getProperties();
        EntityProperty field = fieldMap.get(dtoFieldName);
        EntityClass genericType = field.getGenericType();
        DMybatisLabel.Builder builder;
        if (genericType == null ) {
            //找不到集合泛型 List List<?> List<Object> ， 直接查询数据库实体
            builder = new DMybatisLabel.Builder(prefix, dtoFieldName, child, field.getPropertyClass());
        } else {
            builder = new DMybatisLabel.Builder(prefix, dtoFieldName, child, field.getPropertyClass(), genericType, true);
        }
        addLabel(builder.build());
        return getChildren();
    }

    /**
     * 一对多查询 调用此方法发必需要调用对应的 left join / right join ... 连表方法，否则会报错
     * <p>
     * 举例 UserDO AddressDO 为一对多关系  UserDTO 为结果类
     * <pre>
     *   MPJLambdaQueryWrapper<UserDO> wrapper = new MPJLambdaQueryWrapper();
     *   wrapper.selectAll(UserDO.class)
     *      .selectCollection(AddressDO.class, UserDTO::getAddressListDTO, map -> map
     *           .id(AddressDO::getId, AddressDTO::getId)                 //如果属性名一致 可以传一个
     *           .result(AddressDO::getUserId)                            //如果属性名一致 可以传一个
     *           .result(AddressDO::getAddress, AddressDTO::getAddress))) //如果属性名一致 可以传一个
     *      .leftJoin(AddressDO.class, ...... )
     *      .eq(...)
     *      ...
     * <pre/>
     *
     * 会自动将 AddressDO类中指定的字段 以mybatis<collection>的方式映射到UserDTO.addressListDTO属性中
     *
     * @since 1.3.0
     *
     * @param child      连表数据库实体类
     * @param dtoField   包装类对应的属性
     * @param collection collection标签内容
     * @param <S>        包装类
     * @param <C>        对多数据库实体类
     * @param <Z>        包装类集合泛型
     * @param <F>        包装类集合字段泛型
     */
    default <S, C, Z, F extends java.util.Collection<Z>> Children selectCollection(EntityClass child,
                                                                                   EntityProperty dtoField,
                                                                                   MFunc<DMybatisLabel.Builder> collection) {
        return selectCollection(null, child, dtoField, collection);
    }

    default <S, Z, F extends java.util.Collection<Z>> Children selectCollection(EntityProperty dtoField,
                                                                                MFunc<DMybatisLabelFree.Builder> collection) {
        //自由映射必须存在泛型Z
        String dtoFieldName = dtoField.getPropertyName();//LambdaUtils.getName(dtoField);
//        Class<S> dtoClass = LambdaUtils.getEntityClass(dtoField);
//        FieldCache field = MPJReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
        //获取集合泛型
        EntityClass genericType = dtoField.getGenericType();
        DMybatisLabelFree.Builder builder = new DMybatisLabelFree.Builder(dtoFieldName, dtoField.getPropertyClass(), genericType);
        DMybatisLabelFree.Builder czBuilder = collection.apply(builder);
        addLabel(czBuilder.build());
        return getChildren();
    }

    default <S, C, Z, F extends java.util.Collection<Z>> Children selectCollection(String prefix,
                                                                                   EntityClass child,
                                                                                   EntityProperty dtoField,
                                                                                   MFunc<DMybatisLabel.Builder> collection) {
        String dtoFieldName =dtoField.getPropertyName(); //LambdaUtils.getName(dtoField);
        //Class<S> dtoClass = //LambdaUtils.getEntityClass(dtoField);
//        FieldCache field = MPJReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
//        //获取集合泛型
//        Class<?> genericType = MPJReflectionKit.getGenericType(field.getField());
//        Class<Z> ofType = (Class<Z>) genericType;
        DMybatisLabel.Builder builder = new DMybatisLabel.Builder(prefix, dtoFieldName, child, dtoField.getPropertyClass(), dtoField.getGenericType(), false);
        DMybatisLabel.Builder czBuilder = collection.apply(builder);
        addLabel(czBuilder.build());
        return getChildren();
    }

    /**
     * 对一查询 用法参考 selectCollection
     *
     * @since 1.3.0
     */
    default  Children selectAssociation(EntityClass child, EntityProperty dtoField) {
        return selectAssociation(null, child, dtoField);
    }

    default  Children selectAssociation(String prefix, EntityClass child, EntityProperty dtoField) {
        String dtoFieldName = dtoField.getPropertyName();//LambdaUtils.getName(dtoField);
//        Class<S> dtoClass = LambdaUtils.getEntityClass(dtoField);
//        Map<String, FieldCache> fieldMap = MPJReflectionKit.getFieldMap(dtoClass);
        //FieldCache field = fieldMap.get(dtoFieldName);
        Assert.isFalse(Collection.class.isAssignableFrom(dtoField.getPropertyClass()), "association 不支持集合类");
        DMybatisLabel.Builder builder;
        builder = new DMybatisLabel.Builder(StringUtils.isBlank(prefix) ? null : prefix,
                dtoFieldName, child, dtoField.getPropertyClass(),  dtoField.getGenericType(), true);
        addLabel(builder.build());
        return getChildren();
    }

    /**
     * 对一查询 用法参考 selectCollection
     *
     * @since 1.3.0
     */
    default <S, C, F> Children selectAssociation(EntityClass child, EntityProperty dtoField,
                                                 MFunc<DMybatisLabel.Builder> collection) {
        return selectAssociation(null, child, dtoField, collection);
    }

    default <S, C, F> Children selectAssociation(EntityProperty dtoField,
                                                 MFunc<DMybatisLabelFree.Builder> collection) {
        String dtoFieldName =  dtoField.getPropertyName();//LambdaUtils.getName(dtoField);
//        Class<S> dtoClass = LambdaUtils.getEntityClass(dtoField);
//        FieldCache field = MPJReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
        Assert.isFalse(Collection.class.isAssignableFrom(dtoField.getPropertyClass()), "association 不支持集合类");
        DMybatisLabelFree.Builder builder = new DMybatisLabelFree.Builder(dtoFieldName, dtoField.getPropertyClass(), dtoField.getGenericType());
        DMybatisLabelFree.Builder cfBuilder = collection.apply(builder);
        addLabel(cfBuilder.build());
        return getChildren();
    }

    default <S, C, F> Children selectAssociation(String prefix, EntityClass child, EntityProperty dtoField,
                                                 MFunc<DMybatisLabel.Builder> collection) {
        String dtoFieldName = dtoField.getPropertyName(); //LambdaUtils.getName(dtoField);
        //Class<S> dtoClass = LambdaUtils.getEntityClass(dtoField);
        //FieldCache field = MPJReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
        Assert.isFalse(Collection.class.isAssignableFrom(dtoField.getPropertyClass()), "association 不支持集合类");
        DMybatisLabel.Builder builder = new DMybatisLabel.Builder(StringUtils.isBlank(prefix) ? null : prefix,
                dtoFieldName, child, dtoField.getPropertyClass(), dtoField.getGenericType(), false);
        DMybatisLabel.Builder cfBuilder = collection.apply(builder);
        addLabel(cfBuilder.build());
        return getChildren();
    }
}

